﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace CommonEngine
{
    public static class LinqExtend
    {
        public static Expression Property(this Expression expression, string propertyName)
        {
            return Expression.Property(expression, propertyName);
        }

        public static Expression AndAlso(this Expression left, Expression right)
        {
            return Expression.AndAlso(left, right);
        }

        public static Expression Call(this Expression instance, string methodName, params Expression[] arguments)
        {
            return Expression.Call(instance, instance.Type.GetMethod(methodName), arguments);
        }

        public static Expression GreaterThan(this Expression left, Expression right)
        {
            return Expression.GreaterThan(left, right);
        }

        public static Expression<T> ToLambda<T>(this Expression body, params ParameterExpression[] parameters)
        {
            return Expression.Lambda<T>(body, parameters);
        }

        /// <summary>
        /// 创建一个值恒为 <c>true</c> 的表达式。
        /// </summary>
        /// <typeparam name="T">表达式方法类型</typeparam>
        /// <returns>一个值恒为 <c>true</c> 的表达式。</returns>
        public static Expression<Func<T, bool>> True<T>() { return param => true; }

        /// <summary>
        /// 创建一个值恒为 <c>false</c> 的表达式。
        /// </summary>
        /// <typeparam name="T">表达式方法类型</typeparam>
        /// <returns>一个值恒为 <c>false</c> 的表达式。</returns>
        public static Expression<Func<T, bool>> False<T>() { return param => false; }

        /// <summary>
        /// 组合And,使用 Expression.AndAlso 的方式拼接两个 System.Linq.Expression。
        /// </summary>
        /// <typeparam name="T">表达式方法类型</typeparam>
        /// <param name="left">左边的 System.Linq.Expression 。</param>
        /// <param name="right">右边的 System.Linq.Expression。</param>
        /// <returns>拼接完成的 System.Linq.Expression。</returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
        {
            return left.Compose(right, Expression.AndAlso);
        }

        /// <summary>
        /// 组合Or,使用 Expression.OrElse 的方式拼接两个 System.Linq.Expression。
        /// </summary>
        /// <typeparam name="T">表达式方法类型</typeparam>
        /// <param name="left">左边的 System.Linq.Expression 。</param>
        /// <param name="right">右边的 System.Linq.Expression。</param>
        /// <returns>拼接完成的 System.Linq.Expression。</returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
        {
            return left.Compose(right, Expression.OrElse);
        }

        #region--实现方案--

        static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            var map = first.Parameters
                .Select((f, i) => new { f, s = second.Parameters[i] })
                .ToDictionary(p => p.s, p => p.f);
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }
        private class ParameterRebinder : ExpressionVisitor
        {
            readonly Dictionary<ParameterExpression, ParameterExpression> map;

            ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
            {
                this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
            }

            public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
            {
                return new ParameterRebinder(map).Visit(exp);
            }

            protected override Expression VisitParameter(ParameterExpression p)
            {
                ParameterExpression replacement;

                if (map.TryGetValue(p, out replacement))
                {
                    p = replacement;
                }
                return base.VisitParameter(p);
            }
        }

        #endregion

        #region---框架表达式需求---

        /// <summary>
        /// 输出单个属性表达式 a=>a.Name (适用Linq排序)
        /// T是属性自身的类型--在外面要事先做好判断--属性的实际类型与T不同就无法输出了
        /// </summary>
        public static Expression<Func<U, T>> MakeFuncExp<U, T>(this string proName) where U : class
        {
            PropertyInfo ps = null;
            if (!string.IsNullOrEmpty(proName))
                ps = typeof(U).GetProperty(proName);
            else
                return (a => default(T));

            if (ps == null)
                return (a => default(T));

            ParameterExpression pp_1 = Expression.Parameter(typeof(U), "a");
            LabelTarget lt = Expression.Label(typeof(T));
            LambdaExpression expr = Expression.Lambda(Expression.Convert(Expression.Property(pp_1, ps), typeof(T)),
                new List<ParameterExpression>() { pp_1 });

            var ex = expr as Expression<Func<U, T>>;
            if (ex != null)
                return ex;
            else
                return (a => default(T));
        }

        /// <summary>
        /// 输出主键表达式 a=>a.Name == "val"
        /// </summary>
        public static Expression<Func<U, bool>> MakeEqualExp<U>(this string proName, object Host_Value) where U : class
        {
            PropertyInfo ps = null;
            if (!string.IsNullOrEmpty(proName))
                ps = typeof(U).GetProperty(proName);
            else
                return (a => 1 == 0);

            if (ps == null)
                return (a => 1 == 0);

            if (Host_Value == null)
                return (a => 1 == 0);

            if (ps.PropertyType != Host_Value.GetType())
                return (a => 1 == 0);

            ParameterExpression pp_1 = Expression.Parameter(typeof(U), "a");
            LambdaExpression expr = Expression.Lambda(
                Expression.Equal(Expression.Property(pp_1, ps), Expression.Constant(Host_Value)),
                new List<ParameterExpression>() { pp_1 }
                );
            var ex = expr as Expression<Func<U, bool>>;

            if (ex != null)
                return ex;
            else
                return (a => 1 == 0);
        }

        /// <summary>
        /// 输出集合过滤表达式 a=> Ls.Contains(a.ID)
        /// </summary>
        public static Expression<Func<U, bool>> MakeContainsExp<U>(this string proName, IList Ls) where U : class
        {
            PropertyInfo ps = null;
            if (!string.IsNullOrEmpty(proName))
                ps = typeof(U).GetProperty(proName);
            else
                return (a => 1 == 0);

            if (ps == null)
                return (a => 1 == 0);

            if (Ls == null)
                return (a => 1 == 0);

            MethodInfo m = Ls.GetType().GetMethod("Contains");
            ParameterExpression pp_1 = Expression.Parameter(typeof(U), "a");    //a=>
            Expression expression = Expression.Call
                       (
                          Expression.Constant(Ls),  //Ls
                          m,    // 反射使用.Contains()方法
                          Expression.Property(pp_1, ps)           // .Contains(a.ID)
                       );

            Expression<Func<U, bool>> finalExpression = Expression.Lambda<Func<U, bool>>(expression, new ParameterExpression[] { pp_1 });
            return finalExpression;
        }

        /// <summary>
        /// 输出字符串过滤表达式  c=> false || c.contain("v1") || c.contain("v2")
        /// </summary>
        public static Expression<Func<T, bool>> MakeStrLikeExp<T>(this string proName, params string[] options) where T : class
        {
            if (string.IsNullOrEmpty(proName))
                return (a => false);

            if (options == null || options.Length < 1)
                return (a => false);

            ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=>
            Expression expression = Expression.Constant(false);
            foreach (var optionName in options)
            {
                Expression right = Expression.Call
                       (
                          Expression.Property(left, typeof(T).GetProperty(proName)),  //c.DataSourceName
                          typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 反射使用.Contains()方法                         
                         Expression.Constant(optionName)           // .Contains(optionName)
                       );
                expression = Expression.Or(right, expression);// false || c.DataSourceName.contain("") || c.DataSourceName.contain("") 
            }
            Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
            return finalExpression;
        }

        #endregion

        /// <summary>
        /// 取排序表达式(用于Linq分页)
        /// </summary>
        public static Expression<Func<T, object>> GetPropertyOrderBy<T>(this string sortBy)
            where T : class
        {
            if (string.IsNullOrEmpty(sortBy))
                return null;
            if (sortBy.Equals("undefined"))
                return null;

            return sortBy.MakeFuncExp<T, object>();
        }

    }
}
